home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / cvs-1_3.lha / cvs-1.3 / contrib / pcl-cvs / pcl-cvs.el < prev    next >
Lisp/Scheme  |  1992-04-07  |  48KB  |  1,477 lines

  1. ;;; pcl-cvs.el,v 1.2 1992/04/07 20:49:19 berliner Exp
  2. ;;; pcl-cvs.el -- A Front-end to CVS 1.3 or later.  Release 1.02.
  3. ;;; Copyright (C) 1991, 1992  Per Cederqvist
  4. ;;;
  5. ;;; This program is free software; you can redistribute it and/or modify
  6. ;;; it under the terms of the GNU General Public License as published by
  7. ;;; the Free Software Foundation; either version 2 of the License, or
  8. ;;; (at your option) any later version.
  9. ;;;
  10. ;;; This program is distributed in the hope that it will be useful,
  11. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ;;; GNU General Public License for more details.
  14. ;;;
  15. ;;; You should have received a copy of the GNU General Public License
  16. ;;; along with this program; if not, write to the Free Software
  17. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. ;;;; See below for installation instructions.
  20. ;;;;
  21. ;;;; There is an TeXinfo file that describes this package.  The GNU
  22. ;;;; General Public License is included in that file.  You should read
  23. ;;;; it to get the most from this package.
  24.  
  25. ;;; Don't try to use this with CVS 1.2 or earlier. It won't work. Get
  26. ;;; CVS 1.3.
  27.  
  28. ;;; Mail questions and bug reports to ceder@lysator.liu.se.
  29.  
  30. (require 'cookie)
  31. (provide 'pcl-cvs)
  32.  
  33. ;;; -------------------------------------------------------
  34. ;;;        START OF THINGS TO CHECK WHEN INSTALLING
  35.  
  36. (defvar cvs-program "/usr/gnu/bin/cvs"
  37.   "*Full path to the cvs executable.")
  38.  
  39. (defvar cvs-diff-program "/usr/gnu/bin/diff"
  40.   "*Full path to the diff program.")
  41.  
  42. (defvar cvs-rm-program "/usr/gnu/bin/rm"
  43.   "*Full path to the rm program. Typically /bin/rm.")
  44.  
  45. ;; Uncomment the following line if you are running on 18.57 or earlier.
  46. ;(setq delete-exited-processes nil)
  47. ;; Emacs version 18.57 and earlier is likely to crash if
  48. ;; delete-exited-processes is t, since the sentinel uses lots of
  49. ;; memory, and 18.57 forgets to GCPROT a variable if
  50. ;; delete-exited-processes is t.
  51.  
  52. ;;;         END OF THINGS TO CHECK WHEN INSTALLING
  53. ;;; --------------------------------------------------------
  54.  
  55. (defvar cvs-bakprefix ".#"
  56.   "The prefix that CVS prepends to files when rcsmerge'ing.")
  57.  
  58. (defvar cvs-erase-input-buffer nil
  59.   "*Non-nil if input buffers should be cleared before asking for new info.")
  60.  
  61. (defvar cvs-auto-remove-handled nil
  62.   "*Non-nil if cvs-remove-handled should be called automatically.
  63. If this is set to any non-nil value entries that does not need to be
  64. checked in will be removed from the *cvs* buffer after every cvs-commit
  65. command.")
  66.  
  67. (defconst cvs-cursor-column 14
  68.   "Column to position cursor in in cvs-mode.
  69. Column 0 is left-most column.")
  70.  
  71. (defvar cvs-mode-map nil
  72.   "Keymap for the cvs mode.")
  73.  
  74. (defvar cvs-edit-mode-map nil
  75.   "Keymap for the cvs edit mode (used when editing cvs log messages).")
  76.  
  77. (defvar cvs-buffer-name "*cvs*"
  78.   "Name of the cvs buffer.")
  79.  
  80. (defvar cvs-commit-prompt-buffer "*cvs-commit-message*"
  81.   "Name of buffer in which the user is prompted for a log message when
  82. committing files.")
  83.  
  84. (defvar cvs-temp-buffer-name "*cvs-tmp*"
  85.   "*Name of the cvs temporary buffer.
  86. Output from cvs is placed here by synchronous commands.")
  87.  
  88. (defvar cvs-cvs-diff-flags nil
  89.   "*List of strings to use as flags to pass to ``cvs diff''.
  90. Used by cvs-diff-cvs.
  91. Set this to '("-u") to get a Unidiff format, or '("-c") to get context diffs.")
  92.  
  93. (defvar cvs-status-flags nil
  94.   "*List of strings to pass to ``cvs status''.")
  95.  
  96. (defvar cvs-log-flags nil
  97.   "*List of strings to pass to ``cvs log''.")
  98.  
  99. (defvar cvs-diff-flags nil
  100.   "*List of strings to use as flags to pass to ``diff''.
  101. Do not confuse with cvs-cvs-diff-flags.  Used by cvs-diff-backup.")
  102.  
  103. (defvar cvs-buffers-to-delete nil
  104.   "List of temporary buffers that should be discarded as soon as possible.
  105. Due to a bug in emacs 18.57 the sentinel can't discard them reliably.")
  106.  
  107. ;; You are NOT allowed to disable this message by default.  However, you
  108. ;; are encouraged to inform your users that by adding
  109. ;;    (setq cvs-inhibit-copyright-message t)
  110. ;; to their .emacs they can get rid of it.  Just don't add that line
  111. ;; to your default.el!
  112. (defvar cvs-inhibit-copyright-message nil
  113.   "*Don't display a Copyright message in the ``*cvs*'' buffer.")
  114.  
  115. (defvar cvs-startup-message
  116.   (if cvs-inhibit-copyright-message
  117.       "PCL-CVS release 1.02"
  118.     "PCL-CVS release 1.02.  Copyright (C) 1992 Per Cederqvist
  119. Pcl-cvs comes with absolutely no warranty; for details consult the manual.
  120. This is free software, and you are welcome to redistribute it under certain
  121. conditions; again, consult the TeXinfo manual for details.")
  122.   "*Startup message for CVS.")
  123.  
  124. (defvar cvs-cvs-buffer nil
  125.   "Internal to pcl-cvs.el.
  126. This variable exists in the *cvs-commit-message* buffer and names
  127. the *cvs* buffer.")
  128.  
  129. ;;; The cvs data structure:
  130. ;;;
  131. ;;; When the `cvs update' is ready we parse the output. Every file
  132. ;;; that is affected in some way is added as a cookie of fileinfo
  133. ;;; (as defined below).
  134. ;;;
  135.  
  136. ;;; cvs-fileinfo
  137. ;;;
  138. ;;;  marked         t/nil
  139. ;;;  type         One of
  140. ;;;               UPDATED    - file copied from repository
  141. ;;;               MODIFIED   - modified by you, unchanged in
  142. ;;;                    repository
  143. ;;;               ADDED      - added by you, not yet committed
  144. ;;;               REMOVED    - removed by you, not yet committed
  145. ;;;               CVS-REMOVED- removed, since file no longer exists
  146. ;;;                    in the repository.
  147. ;;;               MERGED     - successful merge
  148. ;;;               CONFLICT   - conflict when merging
  149. ;;;               REM-CONFLICT-removed in repository, changed locally.
  150. ;;;               MOD-CONFLICT-removed locally, changed in repository.
  151. ;;;               DIRCHANGE  - A change of directory.
  152. ;;;               UNKNOWN    - An unknown file.
  153. ;;;               MOVE-AWAY  - A file that is in the way.
  154. ;;;               REPOS-MISSING- The directory is removed from the
  155. ;;;                      repository. Go fetch a backup.
  156. ;;;  dir         Directory the file resides in. Should not end with
  157. ;;;             slash.
  158. ;;;  file-name         The file name.
  159. ;;;  backup-file     Name of the backup file if MERGED or CONFLICT.
  160. ;;;  cvs-diff-buffer     A buffer that contains a 'cvs diff file'.
  161. ;;;  backup-diff-buffer     A buffer that contains a 'diff file backup-file'.
  162. ;;;  full-log         The output from cvs, unparsed.
  163. ;;;  mod-time         Modification time of file used for *-diff-buffer.
  164. ;;;  handled         True if this file doesn't require further action.
  165. ;;; 
  166. ;;; Constructor:
  167.  
  168. ;;; cvs-fileinfo
  169.  
  170. ;;; Constructor:
  171.  
  172. (defun cvs-create-fileinfo (type
  173.                 dir
  174.                 file-name
  175.                 full-log)
  176.   "Create a fileinfo from all parameters.
  177. Arguments: TYPE DIR FILE-NAME FULL-LOG.
  178. A fileinfo has the following fields:
  179.  
  180.   marked        t/nil
  181.   type              One of
  182.             UPDATED       - file copied from repository
  183.             MODIFIED   - modified by you, unchanged in
  184.                      repository
  185.             ADDED       - added by you, not yet committed
  186.             REMOVED       - removed by you, not yet committed
  187.             CVS-REMOVED- removed, since file no longer exists
  188.                      in the repository.
  189.             MERGED       - successful merge
  190.             CONFLICT   - conflict when merging
  191.             REM-CONFLICT-removed in repository, but altered
  192.                      locally.
  193.             MOD-CONFLICT-removed locally, changed in repository.
  194.             DIRCHANGE  - A change of directory.
  195.             UNKNOWN       - An unknown file.
  196.             MOVE-AWAY  - A file that is in the way.
  197.             REPOS-MISSING- The directory has vanished from the
  198.                        repository.
  199.   dir              Directory the file resides in. Should not end with slash.
  200.   file-name          The file name.
  201.   backup-file          Name of the backup file if MERGED or CONFLICT.
  202.   cvs-diff-buffer     A buffer that contains a 'cvs diff file'.
  203.   backup-diff-buffer  A buffer that contains a 'diff file backup-file'.
  204.   full-log          The output from cvs, unparsed.
  205.   mod-time          Modification time of file used for *-diff-buffer.
  206.   handled          True if this file doesn't require further action."
  207.   (cons
  208.    'CVS-FILEINFO
  209.    (vector nil nil type dir file-name nil nil nil full-log nil)))
  210.  
  211.  
  212. ;;; Selectors:
  213.  
  214. (defun cvs-fileinfo->handled (cvs-fileinfo)
  215.   "Get the  `handled' field from CVS-FILEINFO."
  216.   (elt (cdr cvs-fileinfo) 0))
  217.  
  218. (defun cvs-fileinfo->marked (cvs-fileinfo)
  219.   "Check if CVS-FILEINFO is marked."
  220.   (elt (cdr cvs-fileinfo) 1))
  221.  
  222. (defun cvs-fileinfo->type (cvs-fileinfo)
  223.   "Get type from CVS-FILEINFO.
  224. Type is one of UPDATED, MODIFIED, ADDED, REMOVED, CVS-REMOVED, MERGED,
  225. CONFLICT, REM-CONFLICT, MOD-CONFLICT, DIRCHANGE, UNKNOWN, MOVE-AWAY
  226. or REPOS-MISSING."
  227.   (elt (cdr cvs-fileinfo) 2))
  228.  
  229. (defun cvs-fileinfo->dir (cvs-fileinfo)
  230.   "Get dir from CVS-FILEINFO.
  231. The directory name does not end with a slash. "
  232.   (elt (cdr cvs-fileinfo) 3))
  233.  
  234. (defun cvs-fileinfo->file-name (cvs-fileinfo)
  235.   "Get file-name from CVS-FILEINFO."
  236.   (elt (cdr cvs-fileinfo) 4))
  237.  
  238. (defun cvs-fileinfo->backup-file (cvs-fileinfo)
  239.   "Get backup-file from CVS-FILEINFO."
  240.   (elt (cdr cvs-fileinfo) 5))
  241.  
  242. (defun cvs-fileinfo->cvs-diff-buffer (cvs-fileinfo)
  243.   "Get cvs-diff-buffer from CVS-FILEINFO."
  244.   (elt (cdr cvs-fileinfo) 6))
  245.  
  246. (defun cvs-fileinfo->backup-diff-buffer (cvs-fileinfo)
  247.   "Get backup-diff-buffer from CVS-FILEINFO."
  248.   (elt (cdr cvs-fileinfo) 7))
  249.  
  250. (defun cvs-fileinfo->full-log (cvs-fileinfo)
  251.   "Get full-log from CVS-FILEINFO."
  252.   (elt (cdr cvs-fileinfo) 8))
  253.  
  254. (defun cvs-fileinfo->mod-time (cvs-fileinfo)
  255.   "Get mod-time from CVS-FILEINFO."
  256.   (elt (cdr cvs-fileinfo) 9))
  257.  
  258. ;;; Modifiers:
  259.  
  260. (defun cvs-set-fileinfo->handled (cvs-fileinfo newval)
  261.   "Set handled in CVS-FILEINFO to NEWVAL."
  262.   (aset (cdr cvs-fileinfo) 0 newval))
  263.  
  264. (defun cvs-set-fileinfo->marked (cvs-fileinfo newval)
  265.   "Set marked in CVS-FILEINFO to NEWVAL."
  266.   (aset (cdr cvs-fileinfo) 1 newval))
  267.  
  268. (defun cvs-set-fileinfo->type (cvs-fileinfo newval)
  269.   "Set type in CVS-FILEINFO to NEWVAL."
  270.   (aset (cdr cvs-fileinfo) 2 newval))
  271.  
  272. (defun cvs-set-fileinfo->dir (cvs-fileinfo newval)
  273.   "Set dir in CVS-FILEINFO to NEWVAL.
  274. The directory should now end with a slash."
  275.   (aset (cdr cvs-fileinfo) 3 newval))
  276.  
  277. (defun cvs-set-fileinfo->file-name (cvs-fileinfo newval)
  278.   "Set file-name in CVS-FILEINFO to NEWVAL."
  279.   (aset (cdr cvs-fileinfo) 4 newval))
  280.  
  281. (defun cvs-set-fileinfo->backup-file (cvs-fileinfo newval)
  282.   "Set backup-file in CVS-FILEINFO to NEWVAL."
  283.   (aset (cdr cvs-fileinfo) 5 newval))
  284.  
  285. (defun cvs-set-fileinfo->cvs-diff-buffer (cvs-fileinfo newval)
  286.   "Set cvs-diff-buffer in CVS-FILEINFO to NEWVAL."
  287.   (aset (cdr cvs-fileinfo) 6 newval))
  288.  
  289. (defun cvs-set-fileinfo->backup-diff-buffer (cvs-fileinfo newval)
  290.   "Set backup-diff-buffer in CVS-FILEINFO to NEWVAL."
  291.   (aset (cdr cvs-fileinfo) 7 newval))
  292.  
  293. (defun cvs-set-fileinfo->full-log (cvs-fileinfo newval)
  294.   "Set full-log in CVS-FILEINFO to NEWVAL."
  295.   (aset (cdr cvs-fileinfo) 8 newval))
  296.  
  297. (defun cvs-set-fileinfo->mod-time (cvs-fileinfo newval)
  298.   "Set full-log in CVS-FILEINFO to NEWVAL."
  299.   (aset (cdr cvs-fileinfo) 9 newval))
  300.  
  301.  
  302.  
  303. ;;; Predicate:
  304.  
  305. (defun cvs-fileinfo-p (object)
  306.   "Return t if OBJECT is a cvs-fileinfo."
  307.   (eq (car-safe object) 'CVS-FILEINFO))
  308.  
  309. ;;;; End of types.
  310.  
  311. (defun cvs-use-temp-buffer ()
  312.   "Display a temporary buffer in another window and select it.
  313. The selected window will not be changed.  The temporary buffer will
  314. be erased and writable."
  315.  
  316.   (display-buffer (get-buffer-create cvs-temp-buffer-name))
  317.   (set-buffer cvs-temp-buffer-name)
  318.   (setq buffer-read-only nil)
  319.   (erase-buffer))
  320.  
  321. ; Too complicated to handle all the cases that are generated.
  322. ; Maybe later.
  323. ;(defun cvs-examine (directory &optional local)
  324. ;  "Run a 'cvs -n update' in the current working directory.
  325. ;That is, check what needs to be done, but don't change the disc.
  326. ;Feed the output to a *cvs* buffer and run cvs-mode on it.
  327. ;If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
  328. ;  (interactive (list (read-file-name "CVS Update (directory): "
  329. ;                     nil default-directory nil)
  330. ;             current-prefix-arg))
  331. ;  (cvs-do-update directory local 'noupdate))
  332.  
  333. (defun cvs-update (directory &optional local)
  334.   "Run a 'cvs update' in the current working directory. Feed the
  335. output to a *cvs* buffer and run cvs-mode on it.
  336. If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
  337.   (interactive (list (read-file-name "CVS Update (directory): "
  338.                      nil default-directory nil)
  339.              current-prefix-arg))
  340.   (cvs-do-update directory local nil))
  341.  
  342. (defun cvs-filter (predicate list &rest extra-args)
  343.   "Apply PREDICATE to each element on LIST.
  344. Args: PREDICATE LIST &rest EXTRA-ARGS.
  345. Return a new list consisting of those elements that PREDICATE
  346. returns non-nil for.
  347.  
  348. If more than two arguments are given the remaining args are
  349. passed to PREDICATE."
  350.   ;; Avoid recursion - this should work for LONG lists also!
  351.   (let* ((head (cons 'dummy-header nil))
  352.      (tail head))
  353.     (while list
  354.       (if (apply predicate (car list) extra-args)
  355.       (setq tail (setcdr tail (list (car list)))))
  356.       (setq list (cdr list)))
  357.     (cdr head)))
  358.  
  359. (defun cvs-update-no-prompt ()
  360.   "Run cvs update in current directory."
  361.   (interactive)
  362.   (cvs-do-update default-directory nil nil))
  363.  
  364. (defun cvs-do-update (directory local dont-change-disc)
  365.   "Do a 'cvs update' in DIRECTORY.
  366. If LOCAL is non-nil 'cvs update -l' is executed.
  367. If DONT-CHANGE-DISC is non-nil 'cvs -n update' is executed.
  368. Both LOCAL and DONT-CHANGE-DISC may be non-nil simultaneously.
  369.  
  370. *Note*: DONT-CHANGE-DISC does not yet work. The parser gets confused."
  371.   (save-some-buffers)
  372.   (let* ((this-dir (file-name-as-directory (expand-file-name directory)))
  373.      (use-this-window (equal (buffer-name (current-buffer))
  374.                  cvs-buffer-name))
  375.      (update-buffer (generate-new-buffer
  376.              (concat (file-name-nondirectory
  377.                   (substring this-dir 0 -1))
  378.                  "-update")))
  379.      cvs-process args)
  380.  
  381.     ;; The *cvs* buffer is killed to avoid confusion - is the update ready
  382.     ;; or not?
  383.     (if (get-buffer cvs-buffer-name)
  384.     (kill-buffer cvs-buffer-name))
  385.  
  386.     ;; Generate "-n update -l".
  387.     (if local (setq args (list "-l")))
  388.     (setq args (cons "update" args))
  389.     (if dont-change-disc (setq args (cons "-n" args)))
  390.  
  391.     ;; Set up the buffer that receives the output from "cvs update".
  392.     (if use-this-window
  393.     (switch-to-buffer update-buffer)
  394.       (set-buffer update-buffer)
  395.       (display-buffer update-buffer))
  396.  
  397.     (setq default-directory this-dir)
  398.     (setq cvs-process
  399.       (let ((process-connection-type nil)) ; Use a pipe, not a pty.
  400.         (apply 'start-process "cvs" update-buffer cvs-program args)))
  401.  
  402.     (setq mode-line-process
  403.       (concat ": "
  404.           (symbol-name (process-status cvs-process))))
  405.     (set-buffer-modified-p (buffer-modified-p))    ; Update the mode line.
  406.     (set-process-sentinel cvs-process 'cvs-sentinel)
  407.  
  408.     ;; Work around a bug in emacs 18.57 and earlier.
  409.     (setq cvs-buffers-to-delete
  410.       (cvs-delete-unused-temporary-buffers cvs-buffers-to-delete))))
  411.  
  412. (defun cvs-delete-unused-temporary-buffers (list)
  413.   "Delete all buffers on LIST that is not visible.
  414. Return a list of all buffers that still is alive."
  415.  
  416.   (cond
  417.    ((null list) nil)
  418.    ((get-buffer-window (car list))
  419.     (cons (car list)
  420.       (cvs-delete-unused-temporary-buffers (cdr list))))
  421.    (t
  422.     (kill-buffer (car list))
  423.     (cvs-delete-unused-temporary-buffers (cdr list)))))
  424.  
  425.  
  426. (put 'cvs-mode 'mode-class 'special)
  427.  
  428. (defun cvs-mode ()
  429.   "\\<cvs-mode-map>Mode used for pcl-cvs, a frontend to CVS.
  430.  
  431. To get the *cvs* buffer you should use ``\\[cvs-update]''.
  432.  
  433. Full documentation is in the TeXinfo file.  These are the most useful commands:
  434.  
  435. \\[cookie-previous-cookie] Move up.                    \\[cookie-next-cookie] Move down.
  436. \\[cvs-commit]   Commit file.                \\[cvs-update-no-prompt]   Reupdate directory.
  437. \\[cvs-mark]   Mark file/dir.              \\[cvs-unmark]   Unmark file/dir.
  438. \\[cvs-mark-all-files]   Mark all files.             \\[cvs-unmark-all-files]   Unmark all files.
  439. \\[cvs-find-file]   Edit file/run Dired.        \\[cvs-find-file-other-window]   Find file or run Dired in other window.
  440. \\[cvs-remove-handled]   Remove processed entries.   \\[cvs-add-change-log-entry-other-window]   Write ChangeLog in other window.
  441. \\[cvs-add]   Add to repository.          \\[cvs-remove-file]   Remove file.
  442. \\[cvs-diff-cvs]   Diff between base revision. \\[cvs-diff-backup]   Diff backup file.
  443. \\[cvs-acknowledge] Delete line from buffer.    \\[cvs-ignore]   Add file to the .cvsignore file.
  444. \\[cvs-log]   Run ``cvs log''.            \\[cvs-status]   Run ``cvs status''.
  445.  
  446. Entry to this mode runs cvs-mode-hook.
  447. This description is updated for release 1.02 of pcl-cvs.
  448. All bindings:
  449. \\{cvs-mode-map}"
  450.   (interactive)
  451.   (setq major-mode 'cvs-mode)
  452.   (setq mode-name "CVS")
  453.   (setq buffer-read-only nil)
  454.   (buffer-flush-undo (current-buffer))
  455.   (make-local-variable 'goal-column)
  456.   (setq goal-column cvs-cursor-column)
  457.   (use-local-map cvs-mode-map)
  458.   (run-hooks 'cvs-mode-hook))
  459.  
  460. (defun cvs-sentinel (proc msg)
  461.   "Sentinel for the cvs update process.
  462. This is responsible for parsing the output from the cvs update when
  463. it is finished."
  464.   (cond
  465.    ((null (buffer-name (process-buffer proc)))
  466.     ;; buffer killed
  467.     (set-process-buffer proc nil))
  468.    ((memq (process-status proc) '(signal exit))
  469.     (let* ((obuf (current-buffer))
  470.        (omax (point-max))
  471.        (opoint (point)))
  472.       ;; save-excursion isn't the right thing if
  473.       ;;  process-buffer is current-buffer
  474.       (unwind-protect
  475.       (progn
  476.         (set-buffer (process-buffer proc))
  477.         (setq mode-line-process
  478.           (concat ": "
  479.               (symbol-name (process-status proc))))
  480.         (cvs-parse-buffer)
  481.         (setq cvs-buffers-to-delete
  482.           (cons (process-buffer proc) cvs-buffers-to-delete)))
  483.     (set-buffer-modified-p (buffer-modified-p)))
  484.       (if (equal obuf (process-buffer proc))
  485.       nil
  486.     (set-buffer (process-buffer proc))
  487.     (if (< opoint omax)
  488.         (goto-char opoint))
  489.     (set-buffer obuf))))))
  490.  
  491. (defun cvs-skip-line (regexp errormsg &optional arg)
  492.   "Like forward-line, but check that the skipped line matches REGEXP.
  493. If it doesn't match REGEXP (error ERRORMSG) is called.
  494. If optional ARG, a number, is given the ARGth parenthesized expression
  495. in the REGEXP is returned as a string.
  496. Point should be in column 1 when this function is called."
  497.   (cond
  498.    ((looking-at regexp)
  499.     (forward-line 1)
  500.     (if arg
  501.     (buffer-substring (match-beginning arg)
  502.               (match-end arg))))
  503.    (t
  504.     (error errormsg))))
  505.  
  506. (defun cvs-get-current-dir (dirname)
  507.   "Return current working directory, suitable for cvs-parse-buffer.
  508. Args: DIRNAME.
  509. Concatenates default-directory and DIRNAME to form an absolute path."
  510.   (if (string= "." dirname)
  511.       (substring default-directory 0 -1)
  512.     (concat default-directory dirname)))
  513.  
  514.  
  515. (defun cvs-parse-buffer ()
  516.   "Parse the current buffer and select a *cvs* buffer.
  517. Signals an error if unexpected output was detected in the buffer."
  518.   (goto-char (point-min))
  519.   (let ((buf (get-buffer-create cvs-buffer-name))
  520.     (current-dir default-directory)
  521.     (root-dir default-directory)
  522.     (parse-buf (current-buffer)))
  523.  
  524.     (cookie-create
  525.      buf 'cvs-pp cvs-startup-message    ;Se comment above cvs-startup-message.
  526.      "---------- End -----")
  527.  
  528.     (cookie-enter-first
  529.      buf
  530.      (cvs-create-fileinfo
  531.       'DIRCHANGE current-dir
  532.       nil ""))
  533.  
  534.     (while (< (point) (point-max))
  535.       (cond
  536.  
  537.        ;; CVS is descending a subdirectory.
  538.  
  539.        ((looking-at "cvs update: Updating \\(.*\\)$")
  540.     (setq current-dir
  541.           (cvs-get-current-dir
  542.            (buffer-substring (match-beginning 1) (match-end 1))))
  543.  
  544.     ;; Omit empty directories.
  545.     (if (eq (cvs-fileinfo->type (cookie-last buf))
  546.         'DIRCHANGE)
  547.         (cookie-delete-last buf))
  548.  
  549.     (cookie-enter-last
  550.      buf
  551.      (cvs-create-fileinfo
  552.       'DIRCHANGE current-dir
  553.       nil (buffer-substring (match-beginning 0)
  554.                 (match-end 0))))
  555.     (forward-line 1))
  556.  
  557.        ;; File removed, since it is removed (by third party) in repository.
  558.        
  559.        ((or (looking-at "cvs update: warning: \\(.*\\) is not (any longer) \
  560. pertinent")
  561.         (looking-at "cvs update: \\(.*\\) is no longer in the repository"))
  562.     (cookie-enter-last
  563.      buf
  564.      (cvs-create-fileinfo
  565.       'CVS-REMOVED current-dir
  566.       (file-name-nondirectory
  567.        (buffer-substring (match-beginning 1) (match-end 1)))
  568.       (buffer-substring (match-beginning 0)
  569.                 (match-end 0))))
  570.     (forward-line 1))
  571.  
  572.        ;; File removed by you, but recreated by cvs. Ignored.
  573.  
  574.        ((looking-at "cvs update: warning: .* was lost$")
  575.     (forward-line 1))
  576.  
  577.        ;; A file that has been created by you, but added to the cvs
  578.        ;; repository by another.
  579.  
  580.        ((looking-at "^cvs update: move away \\(.*\\); it is in the way$")
  581.     (cookie-enter-last
  582.      buf
  583.      (cvs-create-fileinfo
  584.       'MOVE-AWAY current-dir
  585.       (file-name-nondirectory
  586.        (buffer-substring (match-beginning 1) (match-end 1)))
  587.       (buffer-substring (match-beginning 0)
  588.                 (match-end 0))))
  589.     (forward-line 1))
  590.  
  591.        ;; Empty line. Probably inserted by mistake by user (or developer :-)
  592.        ;; Ignore.
  593.  
  594.        ((looking-at "^$")
  595.     (forward-line 1))
  596.  
  597.        ;; Cvs waits for a lock. Ignore.
  598.  
  599.        ((looking-at
  600.      "^cvs update: \\[..:..:..\\] waiting for .*lock in ")
  601.     (forward-line 1))
  602.  
  603.        ;; File removed in repository, but edited by you.
  604.  
  605.        ((looking-at
  606.      "cvs update: conflict: \\(.*\\) is modified but no longer \
  607. in the repository$")
  608.     (cookie-enter-last
  609.      buf
  610.      (cvs-create-fileinfo
  611.       'REM-CONFLICT current-dir
  612.       (file-name-nondirectory
  613.        (buffer-substring (match-beginning 1) (match-end 1)))
  614.       (buffer-substring (match-beginning 0)
  615.                 (match-end 0))))
  616.     (forward-line 1))
  617.  
  618.        ((looking-at
  619.      "cvs update: conflict: removed \\(.*\\) was modified by second party")
  620.     (cvs-create-fileinfo
  621.      'MOD-CONFLICT current-dir
  622.      (buffer-substring (match-beginning 1) (match-end 1))
  623.      (buffer-substring (match-beginning 0) (match-end 0)))
  624.     (forward-line 1))
  625.  
  626.        ((looking-at "cvs update: in directory ")
  627.     (let ((start (point)))
  628.       (forward-line 1)
  629.       (cvs-skip-line
  630.        (regexp-quote "cvs [update aborted]: there is no repository ")
  631.        "Unexpected cvs output.")
  632.       (cookie-enter-last
  633.        buf
  634.        (cvs-create-fileinfo
  635.         'REPOS-MISSING current-dir
  636.         nil
  637.       (buffer-substring start (point))))))
  638.  
  639.        ;; The file is copied from the repository.
  640.  
  641.        ((looking-at "U \\(.*\\)$")
  642.     (cookie-enter-last
  643.      buf
  644.      (let ((fileinfo
  645.         (cvs-create-fileinfo
  646.          'UPDATED current-dir
  647.          (file-name-nondirectory
  648.           (buffer-substring (match-beginning 1) (match-end 1)))
  649.          (buffer-substring (match-beginning 0) (match-end 0)))))
  650.        (cvs-set-fileinfo->handled fileinfo t)
  651.        fileinfo))
  652.     (forward-line 1))
  653.  
  654.        ;; The file is modified by the user, and untouched in the repository.
  655.  
  656.        ((looking-at "M \\(.*\\)$")
  657.     (cookie-enter-last
  658.      buf
  659.      (cvs-create-fileinfo
  660.       'MODIFIED current-dir
  661.       (file-name-nondirectory
  662.        (buffer-substring (match-beginning 1) (match-end 1)))
  663.       (buffer-substring (match-beginning 0) (match-end 0))))
  664.     (forward-line 1))
  665.  
  666.        ;; The file is "cvs add"ed, but not "cvs ci"ed.
  667.  
  668.        ((looking-at "A \\(.*\\)$")
  669.     (cookie-enter-last
  670.      buf
  671.      (cvs-create-fileinfo
  672.       'ADDED current-dir
  673.       (file-name-nondirectory
  674.        (buffer-substring (match-beginning 1) (match-end 1)))
  675.       (buffer-substring (match-beginning 0) (match-end 0))))
  676.     (forward-line 1))
  677.  
  678.        ;; The file is "cvs remove"ed, but not "cvs ci"ed.
  679.  
  680.        ((looking-at "R \\(.*\\)$")
  681.     (cookie-enter-last
  682.      buf
  683.      (cvs-create-fileinfo
  684.       'REMOVED current-dir
  685.       (file-name-nondirectory
  686.        (buffer-substring (match-beginning 1) (match-end 1)))
  687.       (buffer-substring (match-beginning 0) (match-end 0))))
  688.     (forward-line 1))
  689.  
  690.        ;; Unknown file.
  691.  
  692.        ((looking-at "? \\(.*\\)$")
  693.     (cookie-enter-last
  694.      buf
  695.      (cvs-create-fileinfo
  696.       'UNKNOWN current-dir
  697.       (file-name-nondirectory
  698.        (buffer-substring (match-beginning 1) (match-end 1)))
  699.       (buffer-substring (match-beginning 0) (match-end 0))))
  700.     (forward-line 1))
  701.        (t
  702.  
  703.     ;; CVS has decided to merge someone elses changes into this
  704.     ;; document. This leads to a lot of garbage being printed.
  705.     ;; First there is two lines that contains no information
  706.     ;; that we skip (but we check that we recognize them).
  707.  
  708.     (let ((complex-start (point))
  709.           initial-revision filename)
  710.  
  711.       (cvs-skip-line "^RCS file: .*$" "Parse error.")
  712.       (setq initial-revision
  713.         (cvs-skip-line "^retrieving revision \\(.*\\)$"
  714.                    "Unexpected output from cvs." 1))
  715.       (cvs-skip-line "^retrieving revision .*$"
  716.              "Unexpected output from cvs.")
  717.  
  718.       ;; Get the file name from the next line.
  719.  
  720.       (setq
  721.        filename
  722.        (cvs-skip-line
  723.         "^Merging differences between [0-9.]+ and [0-9.]+ into \\(.*\\)$"
  724.         "Unexpected output from cvs."
  725.         1))
  726.  
  727.       (cond
  728.  
  729.        ;; The file was successfully merged.
  730.  
  731.        ((looking-at "^M ")
  732.         (forward-line 1)
  733.         (let ((fileinfo
  734.            (cvs-create-fileinfo
  735.             'MERGED current-dir
  736.             filename
  737.             (buffer-substring complex-start (point)))))
  738.           (cvs-set-fileinfo->backup-file
  739.            fileinfo
  740.            (concat cvs-bakprefix filename "." initial-revision))
  741.           (cookie-enter-last
  742.            buf fileinfo)))
  743.  
  744.        ;; A conflicting merge.
  745.  
  746.        (t
  747.         (cvs-skip-line "^merge: overlaps during merge$"
  748.                "Unexpected output from cvs.")
  749.         (cvs-skip-line "^cvs update: conflicts found in "
  750.                "Unexpected output from cvs.")
  751.         (cvs-skip-line "^C " "Unexpected cvs output.")
  752.         (let ((fileinfo
  753.            (cvs-create-fileinfo
  754.             'CONFLICT current-dir
  755.             filename
  756.             (buffer-substring complex-start (point)))))
  757.  
  758.           (cvs-set-fileinfo->backup-file
  759.            fileinfo
  760.            (concat cvs-bakprefix filename "." initial-revision))
  761.  
  762.           (cookie-enter-last buf fileinfo))))))))
  763.  
  764.     ;; All parsing is done.
  765.  
  766.     ;; If the last entry is a directory, remove it.
  767.     (if (eq (cvs-fileinfo->type (cookie-last buf))
  768.         'DIRCHANGE)
  769.     (cookie-delete-last buf))
  770.  
  771.     (set-buffer buf)
  772.     (cvs-mode)
  773.     (setq cookie-last-tin (cookie-nth buf 0))
  774.     (goto-char (point-min))
  775.     (cookie-previous-cookie buf (point-min) 1)
  776.     (setq default-directory root-dir)
  777.     (if (get-buffer-window parse-buf)
  778.     (set-window-buffer (get-buffer-window parse-buf) buf)
  779.       (display-buffer buf))))
  780.  
  781.  
  782. (defun cvs-pp (fileinfo)
  783.   "Pretty print FILEINFO into a string."
  784.  
  785.   (let ((a (cvs-fileinfo->type fileinfo))
  786.         (s (if (cvs-fileinfo->marked fileinfo)
  787.                "*" " "))
  788.         (f (cvs-fileinfo->file-name fileinfo))
  789.         (ci (if (cvs-fileinfo->handled fileinfo)
  790.                 "  " "ci")))
  791.     (cond
  792.      ((eq a 'UPDATED)
  793.       (format "%s Updated     %s" s f))
  794.      ((eq a 'MODIFIED)
  795.       (format "%s Modified %s %s" s ci f))
  796.      ((eq a 'MERGED)
  797.       (format "%s Merged   %s %s" s ci f))
  798.      ((eq a 'CONFLICT)
  799.       (format "%s Conflict    %s" s f))
  800.      ((eq a 'ADDED)
  801.       (format "%s Added    %s %s" s ci f))
  802.      ((eq a 'REMOVED)
  803.       (format "%s Removed  %s %s" s ci f))
  804.      ((eq a 'UNKNOWN)
  805.       (format "%s Unknown     %s" s f))
  806.      ((eq a 'CVS-REMOVED)
  807.       (format "%s Removed from repository:  %s" s f))
  808.      ((eq a 'REM-CONFLICT)
  809.       (format "%s Conflict: Removed from repository, changed by you: %s" s f))
  810.      ((eq a 'MOD-CONFLICT)
  811.       (format "%s Conflict: Removed by you, changed in repository: %s" s f))
  812.      ((eq a 'DIRCHANGE)
  813.       (format "\nIn directory %s:"
  814.               (cvs-fileinfo->dir fileinfo)))
  815.      ((eq a 'MOVE-AWAY)
  816.       (format "%s Move away %s - it is in the way" s f))
  817.      ((eq a 'REPOS-MISSING)
  818.       (format "  This repository is missing! Remove this dir manually."))
  819.      (t
  820.       (format "%s Internal error! %s" s f)))))
  821.  
  822.  
  823. ;;; You can define your own keymap in .emacs. pcl-cvs.el won't overwrite it.
  824.  
  825. (if cvs-mode-map
  826.     nil
  827.   (setq cvs-mode-map (make-keymap))
  828.   (suppress-keymap cvs-mode-map)
  829.   (define-key cvs-mode-map " "    'cookie-next-cookie)
  830.   (define-key cvs-mode-map "?"    'describe-mode)
  831.   (define-key cvs-mode-map "A"    'cvs-add-change-log-entry-other-window)
  832.   (define-key cvs-mode-map "M"    'cvs-mark-all-files)
  833.   (define-key cvs-mode-map "U"    'cvs-unmark-all-files)
  834.   (define-key cvs-mode-map "\C-?"  'cvs-unmark-up)
  835.   (define-key cvs-mode-map "\C-n"  'cookie-next-cookie)
  836.   (define-key cvs-mode-map "\C-p"  'cookie-previous-cookie)
  837.   (define-key cvs-mode-map "a"    'cvs-add)
  838.   (define-key cvs-mode-map "b"    'cvs-diff-backup)
  839.   (define-key cvs-mode-map "c"    'cvs-commit)
  840.   (define-key cvs-mode-map "d"    'cvs-diff-cvs)
  841.   (define-key cvs-mode-map "f"    'cvs-find-file)
  842.   (define-key cvs-mode-map "g"    'cvs-update-no-prompt)
  843.   (define-key cvs-mode-map "i"    'cvs-ignore)
  844.   (define-key cvs-mode-map "l"    'cvs-log)
  845.   (define-key cvs-mode-map "m"    'cvs-mark)
  846.   (define-key cvs-mode-map "n"    'cookie-next-cookie)
  847.   (define-key cvs-mode-map "o"    'cvs-find-file-other-window)
  848.   (define-key cvs-mode-map "p"    'cookie-previous-cookie)
  849.   (define-key cvs-mode-map "r"    'cvs-remove-file)
  850.   (define-key cvs-mode-map "s"    'cvs-status)
  851.   (define-key cvs-mode-map "\C-k"  'cvs-acknowledge)
  852.   (define-key cvs-mode-map "x"    'cvs-remove-handled)
  853.   (define-key cvs-mode-map "u"    'cvs-unmark))
  854.  
  855.  
  856. (defun cvs-get-marked ()
  857.   "Return a list of all selected tins.
  858. If there are any marked tins, return them.
  859. Otherwise, if the cursor selects a directory, return all files in it.
  860. Otherwise return (a list containing) the file the cursor points to, or
  861. an empty list if it doesn't point to a file at all."
  862.     
  863.   (cond
  864.    ;; Any marked cookies?
  865.    ((cookie-collect-tins (current-buffer)
  866.              'cvs-fileinfo->marked))
  867.    ;; Nope.
  868.    (t
  869.     (let ((sel (cookie-get-selection
  870.         (current-buffer) (point) cookie-last-tin)))
  871.       (cond
  872.        ;; If a directory is selected, all it members are returned.
  873.        ((and sel (eq (cvs-fileinfo->type
  874.             (cookie-cookie (current-buffer) sel))
  875.                'DIRCHANGE))
  876.     (cookie-collect-tins
  877.      (current-buffer) 'cvs-dir-member-p
  878.      (cvs-fileinfo->dir (cookie-cookie (current-buffer) sel))))
  879.        (t
  880.     (list sel)))))))
  881.  
  882.  
  883. (defun cvs-dir-member-p (fileinfo dir)
  884.   "Return true if FILEINFO represents a file in directory DIR."
  885.   (and (not (eq (cvs-fileinfo->type fileinfo) 'DIRCHANGE))
  886.        (string= (cvs-fileinfo->dir fileinfo) dir)))
  887.  
  888. (defun cvs-dir-empty-p (cvs-buf tin)
  889.   "Return non-nil if TIN is a directory that is empty.
  890. Args: CVS-BUF TIN."
  891.   (and (eq (cvs-fileinfo->type (cookie-cookie cvs-buf tin)) 'DIRCHANGE)
  892.        (or (not (cookie-next cvs-buf tin))
  893.        (eq (cvs-fileinfo->type (cookie-cookie cvs-buf
  894.                           (cookie-next cvs-buf tin)))
  895.            'DIRCHANGE))))
  896.  
  897. (defun cvs-remove-handled ()
  898.   "Remove all lines that are handled.
  899. Empty directories are removed."
  900.   (interactive)
  901.   ;; Pass one: remove files that are handled.
  902.   (cookie-filter (current-buffer)
  903.          (function
  904.           (lambda (fileinfo) (not (cvs-fileinfo->handled fileinfo)))))
  905.   ;; Pass two: remove empty directories.
  906.   (cookie-filter-tins (current-buffer)
  907.               (function
  908.                (lambda (tin)
  909.              (not (cvs-dir-empty-p (current-buffer) tin))))))
  910.  
  911. (defun cvs-mark (pos)
  912.   "Mark a fileinfo. Args: POS.
  913. If the fileinfo is a directory, all the contents of that directory are
  914. marked instead. A directory can never be marked.
  915. POS is a buffer position."
  916.  
  917.   (interactive "d")
  918.  
  919.   (let* ((tin (cookie-get-selection
  920.            (current-buffer) pos cookie-last-tin))
  921.      (sel (cookie-cookie (current-buffer) tin)))
  922.  
  923.     (cond
  924.      ;; Does POS point to a directory? If so, mark all files in that directory.
  925.      ((eq (cvs-fileinfo->type sel) 'DIRCHANGE)
  926.       (cookie-map
  927.        (function (lambda (f dir)
  928.            (cond
  929.             ((cvs-dir-member-p f dir)
  930.              (cvs-set-fileinfo->marked f t)
  931.              t))))        ;Tell cookie to redisplay this cookie.
  932.        (current-buffer)
  933.        (cvs-fileinfo->dir sel)))
  934.      (t
  935.       (cvs-set-fileinfo->marked sel t)
  936.       (cookie-invalidate-tins (current-buffer) tin)
  937.       (cookie-next-cookie (current-buffer) pos 1)))))
  938.   
  939.  
  940. (defun cvs-committable (tin cvs-buf)
  941.   "Check if the TIN is committable.
  942. It is committable if it
  943.    a) is not handled and
  944.    b) is either MODIFIED, ADDED, REMOVED, MERGED or CONFLICT."
  945.   (let* ((fileinfo (cookie-cookie cvs-buf tin))
  946.      (type (cvs-fileinfo->type fileinfo)))
  947.     (and (not (cvs-fileinfo->handled fileinfo))
  948.      (or (eq type 'MODIFIED)
  949.          (eq type 'ADDED)
  950.          (eq type 'REMOVED)
  951.          (eq type 'MERGED)
  952.          (eq type 'CONFLICT)))))
  953.  
  954. (defun cvs-commit ()
  955.  
  956.   "Check in all marked files, or the current file.
  957. The user will be asked for a log message in a buffer.
  958. If cvs-erase-input-buffer is non-nil that buffer will be erased.
  959. Otherwise mark and point will be set around the entire contents of the
  960. buffer so that it is easy to kill the contents of the buffer with \\[kill-region]."
  961.  
  962.   (interactive)
  963.  
  964.   (let* ((cvs-buf (current-buffer))
  965.      (marked (cvs-filter (function cvs-committable)
  966.                  (cvs-get-marked)
  967.                  cvs-buf)))
  968.     (if (null marked)
  969.     (error "Nothing to commit!")
  970.       (pop-to-buffer (get-buffer-create cvs-commit-prompt-buffer))
  971.       (goto-char (point-min))
  972.  
  973.       (if cvs-erase-input-buffer
  974.       (erase-buffer)
  975.     (push-mark (point-max)))
  976.       (cvs-edit-mode)
  977.       (make-local-variable 'cvs-commit-list)
  978.       (setq cvs-commit-list marked)
  979.       (make-local-variable 'cvs-cvs-buffer)
  980.       (setq cvs-cvs-buffer cvs-buf)
  981.       (message "Press C-c C-c when you are done editing."))))
  982.  
  983.  
  984. (defun cvs-edit-done ()
  985.   "Commit the files to the repository."
  986.   (interactive)
  987.   (save-some-buffers)
  988.   (let ((cc-list cvs-commit-list)
  989.     (cc-buffer cvs-cvs-buffer)
  990.     (msg-buffer (current-buffer))
  991.     (msg (buffer-substring (point-min) (point-max))))
  992.     (pop-to-buffer cc-buffer)
  993.     (bury-buffer msg-buffer)
  994.     (cvs-use-temp-buffer)
  995.     (message "Committing...")
  996.     (cvs-execute-list cc-list cvs-program (list "commit" "-m" msg))
  997.     (mapcar (function
  998.          (lambda (tin)
  999.            (cvs-set-fileinfo->handled (cookie-cookie cc-buffer tin) t)))
  1000.         cc-list)
  1001.     (apply 'cookie-invalidate-tins cc-buffer cc-list)
  1002.     (set-buffer cc-buffer)
  1003.     (if cvs-auto-remove-handled
  1004.     (cvs-remove-handled)))
  1005.   
  1006.   (message "Committing... Done."))
  1007.  
  1008.  
  1009. (defun cvs-execute-list (tin-list program constant-args)
  1010.   "Run PROGRAM on all elements on TIN-LIST.
  1011. Args: TIN-LIST PROGRAM CONSTANT-ARGS
  1012. The PROGRAM will be called with pwd set to the directory the
  1013. files reside in. CONSTANT-ARGS should be a list of strings. The
  1014. arguments given to the program will be CONSTANT-ARGS followed by all
  1015. the files (from TIN-LIST) that resides in that directory. If the files
  1016. in TIN-LIST resides in different directories the PROGRAM will be run
  1017. once for each directory (if all files in the same directory appears
  1018. after each other."
  1019.              
  1020.     (while tin-list
  1021.       (let ((current-dir (cvs-fileinfo->dir
  1022.               (cookie-cookie cvs-buffer-name
  1023.                      (car tin-list))))
  1024.         arg-list arg-str)
  1025.  
  1026.     ;; Collect all marked files in this directory.
  1027.  
  1028.     (while (and tin-list
  1029.             (string=
  1030.              current-dir
  1031.              (cvs-fileinfo->dir
  1032.               (cookie-cookie cvs-buffer-name (car tin-list)))))
  1033.       (setq arg-list
  1034.         (cons (cvs-fileinfo->file-name
  1035.                (cookie-cookie cvs-buffer-name (car tin-list)))
  1036.               arg-list))
  1037.       (setq tin-list (cdr tin-list)))
  1038.  
  1039.     (setq arg-list (nreverse arg-list))
  1040.  
  1041.     ;; Execute the command on all the files that were collected.
  1042.  
  1043.     (setq default-directory (file-name-as-directory current-dir))
  1044.     (insert (format "=== cd %s\n" default-directory))
  1045.     (insert (format "=== %s %s\n\n"
  1046.             program
  1047.             (mapconcat '(lambda (foo) foo)
  1048.                    (nconc (copy-sequence constant-args)
  1049.                       arg-list)
  1050.                    " ")))
  1051.     (apply 'call-process program nil t t
  1052.            (nconc (copy-sequence constant-args) arg-list))
  1053.     (goto-char (point-max)))))
  1054.  
  1055.  
  1056. (defun cvs-execute-single-file-list (tin-list extractor program constant-args)
  1057.   "Run PROGRAM on all elements on TIN-LIST.
  1058.  
  1059. Args: TIN-LIST EXTRACTOR PROGRAM CONSTANT-ARGS
  1060.  
  1061. The PROGRAM will be called with pwd set to the directory the files
  1062. reside in.  CONSTANT-ARGS is a list of strings to pass as arguments to
  1063. PROGRAM.  The arguments given to the program will be CONSTANT-ARGS
  1064. followed by the list that EXTRACTOR returns.
  1065.  
  1066. EXTRACTOR will be called once for each file on TIN-LIST.  It is given
  1067. one argument, the cvs-fileinfo.  It can return t, which means ignore
  1068. this file, or a list of arguments to send to the program."
  1069.  
  1070.     (while tin-list
  1071.       (let ((default-directory (file-name-as-directory
  1072.                 (cvs-fileinfo->dir
  1073.                  (cookie-cookie cvs-buffer-name
  1074.                            (car tin-list)))))
  1075.         (arg-list
  1076.          (funcall extractor
  1077.               (cookie-cookie cvs-buffer-name (car tin-list)))))
  1078.  
  1079.     ;; Execute the command unless extractor returned t.
  1080.  
  1081.     (if (eq arg-list t)
  1082.         nil
  1083.       (insert (format "=== cd %s\n" default-directory))
  1084.       (insert (format "=== %s %s\n\n"
  1085.               program
  1086.               (mapconcat '(lambda (foo) foo)
  1087.                      (nconc (copy-sequence constant-args)
  1088.                         arg-list)
  1089.                      " ")))
  1090.       (apply 'call-process program nil t t
  1091.          (nconc (copy-sequence constant-args) arg-list))
  1092.       (goto-char (point-max))))
  1093.       (setq tin-list (cdr tin-list))))
  1094.  
  1095.  
  1096. (defun cvs-edit-mode ()
  1097.   "\\<cvs-edit-mode-map>Mode for editing cvs log messages.
  1098. Commands:
  1099. \\[cvs-edit-done] checks in the file when you are ready.
  1100. This mode is based on fundamental mode."
  1101.   (interactive)
  1102.   (use-local-map cvs-edit-mode-map)
  1103.   (setq major-mode 'cvs-edit-mode)
  1104.   (setq mode-name "CVS Log")
  1105.   (auto-fill-mode 1))
  1106.  
  1107.  
  1108. (if cvs-edit-mode-map
  1109.     nil
  1110.   (setq cvs-edit-mode-map (make-sparse-keymap))
  1111.   (define-prefix-command 'cvs-control-c-prefix)
  1112.   (define-key cvs-edit-mode-map "\C-c" 'cvs-control-c-prefix)
  1113.   (define-key cvs-edit-mode-map "\C-c\C-c" 'cvs-edit-done))
  1114.  
  1115.  
  1116. (defun cvs-diff-cvs ()
  1117.   "Diff the selected files against the repository.
  1118. The flags the variable cvs-cvs-diff-flags will be passed to ``cvs diff''."
  1119.   (interactive)
  1120.  
  1121.   (save-some-buffers)
  1122.   (let ((marked (cvs-get-marked)))
  1123.     (cvs-use-temp-buffer)
  1124.     (message "cvsdiffing...")
  1125.     (cvs-execute-list marked cvs-program (cons "diff" cvs-cvs-diff-flags)))
  1126.   (message "cvsdiffing... Done."))
  1127.  
  1128.  
  1129. (defun cvs-backup-diffable (tin cvs-buf)
  1130.   "Check if the TIN is backup-diffable.
  1131. It must have a backup file to be diffable."
  1132.   (cvs-fileinfo->backup-file (cookie-cookie cvs-buf tin)))
  1133.  
  1134. (defun cvs-diff-backup ()
  1135.   "Diff the files against the backup file.
  1136. This command can be used on files that are marked with \"Merged\"
  1137. or \"Conflict\" in the *cvs* buffer.
  1138.  
  1139. The flags in cvs-diff-flags will be passed to ``diff''."
  1140.  
  1141.   (interactive)
  1142.   (save-some-buffers)
  1143.   (let ((marked (cvs-filter (function cvs-backup-diffable)
  1144.                 (cvs-get-marked)
  1145.                 (current-buffer))))
  1146.     (if (null marked)
  1147.     (error "No ``Conflict'' or ``Merged'' file selected!"))
  1148.     (cvs-use-temp-buffer)
  1149.     (message "diffing...")
  1150.     (cvs-execute-single-file-list
  1151.      marked 'cvs-diff-backup-extractor cvs-diff-program cvs-diff-flags))
  1152.   (message "diffing... Done."))
  1153.  
  1154.  
  1155. (defun cvs-diff-backup-extractor (fileinfo)
  1156.   "Return the filename and the name of the backup file as a list.
  1157. Signal an error if there is no backup file."
  1158.   (if (null (cvs-fileinfo->backup-file fileinfo))
  1159.       (error "%s has no backup file."
  1160.          (concat
  1161.           (file-name-as-directory (cvs-fileinfo->dir fileinfo))
  1162.           (cvs-fileinfo->file-name fileinfo))))
  1163.   (list (cvs-fileinfo->file-name fileinfo)
  1164.     (cvs-fileinfo->backup-file fileinfo)))
  1165.  
  1166. (defun cvs-find-file-other-window (pos)
  1167.   "Select a buffer containing the file in another window.
  1168. Args: POS"
  1169.   (interactive "d")
  1170.   (save-some-buffers)
  1171.   (let* ((cookie-last-tin
  1172.       (cookie-get-selection (current-buffer) pos cookie-last-tin))
  1173.      (type (cvs-fileinfo->type (cookie-cookie (current-buffer)
  1174.                           cookie-last-tin))))
  1175.     (cond
  1176.      ((or (eq type 'REMOVED)
  1177.       (eq type 'CVS-REMOVED))
  1178.       (error "Can't visit a removed file."))
  1179.      ((eq type 'DIRCHANGE)
  1180.       (let ((obuf (current-buffer))
  1181.         (odir default-directory))
  1182.     (setq default-directory
  1183.           (file-name-as-directory
  1184.            (cvs-fileinfo->dir
  1185.         (cookie-cookie (current-buffer) cookie-last-tin))))
  1186.     (dired-other-window default-directory)
  1187.     (set-buffer obuf)
  1188.     (setq default-directory odir))) 
  1189.      (t
  1190.       (find-file-other-window (cvs-full-path (current-buffer)
  1191.                          cookie-last-tin))))))
  1192.  
  1193. (defun cvs-full-path (buffer tin)
  1194.   "Return the full path for the file that is described in TIN.
  1195. Args: BUFFER TIN."
  1196.   (concat
  1197.    (file-name-as-directory
  1198.     (cvs-fileinfo->dir (cookie-cookie buffer tin)))
  1199.    (cvs-fileinfo->file-name (cookie-cookie buffer tin))))
  1200.  
  1201. (defun cvs-find-file (pos)
  1202.   "Select a buffer containing the file in another window.
  1203. Args: POS"
  1204.   (interactive "d")
  1205.   (let* ((cvs-buf (current-buffer))
  1206.      (cookie-last-tin (cookie-get-selection cvs-buf pos cookie-last-tin))
  1207.      (fileinfo (cookie-cookie cvs-buf cookie-last-tin))
  1208.      (type (cvs-fileinfo->type fileinfo)))
  1209.     (cond
  1210.      ((or (eq type 'REMOVED)
  1211.       (eq type 'CVS-REMOVED))
  1212.       (error "Can't visit a removed file."))
  1213.      ((eq type 'DIRCHANGE)
  1214.       (let ((odir default-directory))
  1215.     (setq default-directory
  1216.           (file-name-as-directory (cvs-fileinfo->dir fileinfo)))
  1217.     (dired default-directory)
  1218.     (set-buffer cvs-buf)
  1219.     (setq default-directory odir))) 
  1220.      (t
  1221.       (find-file (cvs-full-path cvs-buf cookie-last-tin))))))
  1222.  
  1223. (defun cvs-mark-all-files ()
  1224.   "Mark all files.
  1225. Directories are not marked."
  1226.   (interactive)
  1227.   (cookie-map (function (lambda (cookie)
  1228.               (cond
  1229.                ((not (eq (cvs-fileinfo->type cookie) 'DIRCHANGE))
  1230.                 (cvs-set-fileinfo->marked cookie t)
  1231.                 t))))
  1232.           (current-buffer)))
  1233.  
  1234.  
  1235. (defun cvs-unmark (pos)
  1236.   "Unmark a fileinfo. Args: POS."
  1237.   (interactive "d")
  1238.  
  1239.   (let* ((tin (cookie-get-selection
  1240.            (current-buffer) pos cookie-last-tin))
  1241.      (sel (cookie-cookie (current-buffer) tin)))
  1242.  
  1243.     (cond
  1244.      ((eq (cvs-fileinfo->type sel) 'DIRCHANGE)
  1245.       (cookie-map
  1246.        (function (lambda (f dir)
  1247.            (cond
  1248.             ((cvs-dir-member-p f dir)
  1249.              (cvs-set-fileinfo->marked f nil)
  1250.              t))))
  1251.        (current-buffer)
  1252.        (cvs-fileinfo->dir sel)))
  1253.      (t
  1254.       (cvs-set-fileinfo->marked sel nil)
  1255.       (cookie-invalidate-tins (current-buffer) tin)
  1256.       (cookie-next-cookie (current-buffer) pos 1)))))
  1257.  
  1258. (defun cvs-unmark-all-files ()
  1259.   "Unmark all files.
  1260. Directories are also unmarked, but that doesn't matter, since
  1261. they should always be unmarked."
  1262.   (interactive)
  1263.   (cookie-map (function (lambda (cookie)
  1264.               (cvs-set-fileinfo->marked cookie nil)
  1265.               t))
  1266.           (current-buffer)))
  1267.  
  1268.  
  1269. (defun cvs-do-removal (cvs-buf tins)
  1270.   "Remove files.
  1271. Args: CVS-BUF TINS.
  1272. CVS-BUF is the cvs buffer. TINS is a list of tins that the
  1273. user wants to delete. The files are deleted. If the type of
  1274. the tin is 'UNKNOWN the tin is removed from the buffer. If it
  1275. is anything else the file is added to a list that should be
  1276. `cvs remove'd and the tin is changed to be of type 'REMOVED.
  1277.  
  1278. Returns a list of tins files that should be `cvs remove'd."
  1279.   (cvs-use-temp-buffer)
  1280.   (mapcar 'cvs-insert-full-path tins)
  1281.   (cond
  1282.    ((and tins (yes-or-no-p (format "Delete %d files? " (length tins))))
  1283.     (let (files-to-remove)
  1284.       (while tins
  1285.     (let* ((tin (car tins))
  1286.            (fileinfo (cookie-cookie cvs-buf tin))
  1287.            (type (cvs-fileinfo->type fileinfo)))
  1288.       (if (not (or (eq type 'REMOVED) (eq type 'CVS-REMOVED)))
  1289.           (progn
  1290.         (delete-file (cvs-full-path cvs-buf tin))
  1291.         (cond
  1292.          ((or (eq type 'UNKNOWN) (eq type 'MOVE-AWAY))
  1293.           (cookie-delete cvs-buf tin))
  1294.          (t
  1295.           (setq files-to-remove (cons tin files-to-remove))
  1296.           (cvs-set-fileinfo->type fileinfo 'REMOVED)
  1297.           (cvs-set-fileinfo->handled fileinfo nil)
  1298.           (cookie-invalidate-tins cvs-buf tin))))))
  1299.     (setq tins (cdr tins)))
  1300.       files-to-remove))
  1301.    (t nil)))
  1302.  
  1303.  
  1304.  
  1305. (defun cvs-remove-file ()
  1306.   "Remove all marked files."
  1307.   (interactive)
  1308.   (let ((files-to-remove (cvs-do-removal (current-buffer) (cvs-get-marked))))
  1309.     (if (null files-to-remove)
  1310.     nil
  1311.       (cvs-use-temp-buffer)
  1312.       (message "removing from repository...")
  1313.       (cvs-execute-list files-to-remove cvs-program '("remove"))
  1314.       (message "removing from repository... done."))))
  1315.  
  1316. (defun cvs-acknowledge ()
  1317.   "Remove all marked files from the buffer."
  1318.   (interactive)
  1319.  
  1320.   (mapcar (function (lambda (tin)
  1321.                (cookie-delete (current-buffer) tin)))
  1322.       (cvs-get-marked))
  1323.   (setq cookie-last-tin nil))
  1324.  
  1325.  
  1326. (defun cvs-unmark-up (pos)
  1327.   "Unmark the file on the previous line.
  1328. Takes one argument POS, a buffer position."
  1329.   (interactive "d")
  1330.   (cookie-previous-cookie (current-buffer) pos 1)
  1331.   (cvs-set-fileinfo->marked (cookie-cookie (current-buffer) cookie-last-tin)
  1332.                 nil)
  1333.   (cookie-invalidate-tins (current-buffer) cookie-last-tin))
  1334.  
  1335. (defun cvs-add-file-update-buffer (cvs-buf tin)
  1336.   "Subfunction to cvs-add. Internal use only.
  1337. Update the display. Return non-nil if `cvs add' should be called on this
  1338. file. Args: CVS-BUF TIN.
  1339. Returns 'ADD or 'RESURRECT."
  1340.   (let ((fileinfo (cookie-cookie cvs-buf tin)))
  1341.     (cond
  1342.      ((eq (cvs-fileinfo->type fileinfo) 'UNKNOWN)
  1343.       (cvs-set-fileinfo->type fileinfo 'ADDED)
  1344.       (cookie-invalidate-tins cvs-buf tin)
  1345.       'ADD)
  1346.      ((eq (cvs-fileinfo->type fileinfo) 'REMOVED)
  1347.       (cvs-set-fileinfo->type fileinfo 'UPDATED)
  1348.       (cvs-set-fileinfo->handled fileinfo t)
  1349.       (cookie-invalidate-tins cvs-buf tin)
  1350.       'RESURRECT))))
  1351.  
  1352. (defun cvs-add-sub (cvs-buf candidates)
  1353.   "Internal use only.
  1354. Args: CVS-BUF CANDIDATES.
  1355. CANDIDATES is a list of tins. Updates the CVS-BUF and returns a pair of lists.
  1356. The first list is unknown tins that shall be `cvs add -m msg'ed.
  1357. The second list is removed files that shall be `cvs add'ed (resurrected)."
  1358.   (let (add resurrect)
  1359.     (while candidates
  1360.       (let ((type (cvs-add-file-update-buffer cvs-buf (car candidates))))
  1361.     (cond ((eq type 'ADD)
  1362.            (setq add (cons (car candidates) add)))
  1363.           ((eq type 'RESURRECT)
  1364.            (setq resurrect (cons (car candidates) resurrect)))))
  1365.       (setq candidates (cdr candidates)))
  1366.     (cons add resurrect)))
  1367.  
  1368. (defun cvs-add ()
  1369.   "Add marked files to the cvs repository."
  1370.   (interactive)
  1371.  
  1372.   (let* ((buf (current-buffer))
  1373.      (result (cvs-add-sub buf (cvs-get-marked)))
  1374.      (added (car result))
  1375.      (resurrect (cdr result))
  1376.      (msg (if added (read-from-minibuffer "Enter description: "))))
  1377.  
  1378.     (if (or resurrect added)
  1379.     (cvs-use-temp-buffer))
  1380.  
  1381.     (cond (resurrect
  1382.        (message "Resurrecting files from repository...")
  1383.        (cvs-execute-list resurrect cvs-program '("add"))
  1384.        (message "Done.")))
  1385.  
  1386.     (cond (added
  1387.        (message "Adding new files to repository...")
  1388.        (cvs-execute-list added cvs-program (list "add" "-m" msg))
  1389.        (message "Done.")))))
  1390.  
  1391. (defun cvs-ignore ()
  1392.   "Arrange so that CVS ignores the selected files.
  1393. This command ignores files that are not flagged as `Unknown'."
  1394.   (interactive)
  1395.  
  1396.   (mapcar (function (lambda (tin)
  1397.               (cond
  1398.                ((eq (cvs-fileinfo->type
  1399.                  (cookie-cookie (current-buffer) tin)) 'UNKNOWN)
  1400.             (cvs-append-to-ignore
  1401.              (cookie-cookie (current-buffer) tin))
  1402.             (cookie-delete (current-buffer) tin)))))
  1403.       (cvs-get-marked))
  1404.   (setq cookie-last-tin nil))
  1405.  
  1406. (defun cvs-append-to-ignore (fileinfo)
  1407.   "Append the file in fileinfo to the .cvsignore file"
  1408.   (save-window-excursion
  1409.     (set-buffer (find-file-noselect (concat (file-name-as-directory
  1410.                          (cvs-fileinfo->dir fileinfo))
  1411.                         ".cvsignore")))
  1412.     (goto-char (point-max))
  1413.     (if (not (zerop (current-column)))
  1414.     (insert "\n"))
  1415.     (insert (cvs-fileinfo->file-name fileinfo) "\n")
  1416.     (save-buffer)))
  1417.  
  1418. (defun cvs-status ()
  1419.   "Show cvs status for all marked files."
  1420.   (interactive)
  1421.  
  1422.   (save-some-buffers)
  1423.   (let ((marked (cvs-get-marked)))
  1424.     (cvs-use-temp-buffer)
  1425.     (message "Running cvs status ...")
  1426.     (cvs-execute-list marked cvs-program (cons "status" cvs-status-flags)))
  1427.   (message "Running cvs status ... Done."))
  1428.  
  1429. (defun cvs-log ()
  1430.   "Display the cvs log of all selected files."
  1431.   (interactive)
  1432.  
  1433.   (let ((marked (cvs-get-marked)))
  1434.     (cvs-use-temp-buffer)
  1435.     (message "Running cvs log ...")
  1436.     (cvs-execute-list marked cvs-program (cons "log" cvs-log-flags)))
  1437.   (message "Running cvs log ... Done."))
  1438.  
  1439.  
  1440. (defun cvs-insert-full-path (tin)
  1441.   "Insert full path to the file described in TIN."
  1442.   (insert (format "%s\n" (cvs-full-path cvs-buffer-name tin))))
  1443.  
  1444.  
  1445. (defun cvs-add-change-log-entry-other-window (pos)
  1446.   "Add a ChangeLog entry in the ChangeLog of the current directory.
  1447. Args: POS."
  1448.   (interactive "d")
  1449.   (let* ((cvs-buf (current-buffer))
  1450.      (odir default-directory))
  1451.     (setq default-directory
  1452.       (file-name-as-directory
  1453.        (cvs-fileinfo->dir
  1454.         (cookie-cookie
  1455.          cvs-buf
  1456.          (cookie-get-selection cvs-buf pos cookie-last-tin)))))
  1457.     (if (not default-directory)        ;In case there was no entries.
  1458.     (setq default-directory odir))
  1459.     (add-change-log-entry-other-window)
  1460.     (set-buffer cvs-buf)
  1461.     (setq default-directory odir)))
  1462.  
  1463.  
  1464. (defun print-cvs-tin (foo)
  1465.   "Debug utility."
  1466.   (let ((cookie (cookie-cookie (current-buffer) foo))
  1467.     (stream (get-buffer-create "debug")))
  1468.     (princ "==============\n" stream)
  1469.     (princ (cvs-fileinfo->file-name cookie) stream)
  1470.     (princ "\n" stream)
  1471.     (princ (cvs-fileinfo->dir cookie) stream)
  1472.     (princ "\n" stream)
  1473.     (princ (cvs-fileinfo->full-log cookie) stream)
  1474.     (princ "\n" stream)
  1475.     (princ (cvs-fileinfo->marked cookie) stream)
  1476.     (princ "\n" stream)))
  1477.